home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / telecomm / sticpsrc.lzh / SOURCE.ARC / FTPSERV.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-02  |  20.6 KB  |  844 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define LINELEN        128    /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include <ctype.h>
  7. #include "global.h"
  8. #include "mbuf.h"
  9. #include "netuser.h"
  10. #include "timer.h"
  11. #include "tcp.h"
  12. #include "ftp.h"
  13. #ifdef ATARI_ST
  14. # ifdef __TURBOC__
  15. #  include <tos.h>
  16. # else
  17. #  include <osbind.h>
  18. # endif
  19. #endif
  20.  
  21. /* Command table */
  22. static char *commands[] = {
  23.     "user",
  24. #define USER_CMD    0
  25.     "acct",
  26. #define ACCT_CMD    1
  27.     "pass",
  28. #define PASS_CMD    2
  29.     "type",
  30. #define TYPE_CMD    3
  31.     "list",
  32. #define LIST_CMD    4
  33.     "cwd",
  34. #define CWD_CMD        5
  35.     "dele",
  36. #define DELE_CMD    6
  37.     "name",
  38. #define NAME_CMD    7
  39.     "quit",
  40. #define QUIT_CMD    8
  41.     "retr",
  42. #define RETR_CMD    9
  43.     "stor",
  44. #define STOR_CMD    10
  45.     "port",
  46. #define PORT_CMD    11
  47.     "nlst",
  48. #define NLST_CMD    12
  49.     "pwd",
  50. #define PWD_CMD        13
  51.     "xpwd",            /* For compatibility with 4.2BSD */
  52. #define XPWD_CMD    14
  53.     "mkd ",
  54. #define MKD_CMD        15
  55.     "xmkd",            /* For compatibility with 4.2BSD */
  56. #define XMKD_CMD    16
  57.     "xrmd",            /* For compatibility with 4.2BSD */
  58. #define XRMD_CMD    17
  59.     "rmd ",
  60. #define RMD_CMD        18
  61.     "stru",
  62. #define STRU_CMD    19
  63.     "mode",
  64. #define MODE_CMD    20
  65. #if (defined(ATARI_ST))
  66.     "xatr",            /* own extension: set attribs */
  67. #define XATR_CMD    21
  68. #endif
  69.     NULLCHAR
  70. };
  71.  
  72. /* Response messages */
  73. static char sending[]    = "150 Opening data connection for %s \"%s\"\r\n";
  74. #if (defined(ATARI_ST))
  75. static char xatrok[]    = "200 \"%s\" XATR ok\r\n";
  76. #endif
  77. static char okay[]    = "200 Ok\r\n";
  78. static char portok[]    = "200 Port command okay\r\n";
  79. static char typeok[]    = "200 Type OK\r\n";
  80. static char banner[]    = "220 %s FTP version %s ready at %.24s\r\n";
  81. static char bye[]    = "221 Goodbye!\r\n";
  82. static char rxok[]    = "226 File received OK\r\n";
  83. static char txok[]    = "226 File sent OK\r\n";
  84. static char logged[]    = "230 Logged in\r\n";
  85. static char deleok[]    = "250 File deleted\r\n";
  86. static char mkdok[]    = "257 \"%s\" directory created\r\n";
  87. static char pwdmsg[]    = "257 \"%s\" is current directory\r\n";
  88. static char givepass[]    = "331 Enter PASS command\r\n";
  89. static char noconn[]    = "425 Data connection reset\r\n";
  90. static char badcmd[]    = "500 Unknown command\r\n";
  91. static char unsupp[]    = "500 Unsupported command or option\r\n";
  92. static char badport[]    = "501 Bad port syntax\r\n";
  93. #if (defined(ATARI_ST))
  94. static char badxatr[]    = "501 Bad XATR syntax\r\n";
  95. #endif
  96. static char only8[]    = "501 Only logical bytesize 8 supported\r\n";
  97. static char badtype[]    = "501 Unknown type \"%s\"\r\n";
  98. static char unimp[]    = "502 Command not yet implemented\r\n";
  99. static char notlog[]    = "530 Please log in with USER and PASS\r\n";
  100. #if (defined(ATARI_ST))
  101. static char xatrfail[]    = "550 \"%s\" XATR failed\r\n";
  102. #endif
  103. static char cantopen[]    = "550 Can't read file \"%s\"\r\n";
  104. static char delefail[]    = "550 Delete failed\r\n";
  105. static char noperm[]    = "550 Permission denied\r\n";
  106. static char cantmake[]    = "553 Can't create \"%s\"\r\n";
  107. static char nodir[]    = "553 Can't read directory \"%s\"\r\n";
  108.  
  109. static struct tcb *ftp_tcb;
  110.  
  111. /* Start up FTP service */
  112. ftp1(argc,argv)
  113. int argc;
  114. char *argv[];
  115. {
  116.     struct socket lsocket;
  117.     void ftpscr(),ftpscs();
  118.     char tos = 0;
  119.  
  120.     lsocket.address = ip_addr;
  121.     if(argc < 2)
  122.         lsocket.port = FTP_PORT;
  123.     else {
  124.         if((lsocket.port = atoi(argv[1])) == 0)
  125.             lsocket.port = FTP_PORT;
  126.         tos = get_tos(argv[2]);
  127.     }
  128.  
  129.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,tos,(char *)NULL);
  130.     return 0;
  131. }
  132. ftp0()
  133. {
  134.     if(ftp_tcb != NULLTCB)
  135.         close_tcp(ftp_tcb);
  136.     return 0;
  137. }
  138. /* FTP Server Control channel State change upcall handler */
  139. static
  140. void
  141. ftpscs(tcb,old,new)
  142. struct tcb *tcb;
  143. char old,new;
  144. {
  145.     extern char hostname[],version[];
  146.     struct ftp *ftp,*ftp_create();
  147.     void ftp_delete();
  148.     long t;
  149.  
  150.     switch(new){
  151. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  152.  * leaving it unset waits for the three-way handshake to complete before
  153.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  154.  * so its use is not (yet) recommended.
  155. */
  156. #ifdef    QUICKSTART
  157.     case SYN_RECEIVED:
  158. #else
  159.     case ESTABLISHED:
  160. #endif
  161.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  162.             /* No space, kill connection */
  163.             close_tcp(tcb);
  164.             return;
  165.         }
  166.         ftp->control = tcb;        /* Downward link */
  167.         tcb->user = (char *)ftp;    /* Upward link */
  168.  
  169.         /* Set default data port */
  170.         ftp->port.address = tcb->conn.remote.address;
  171.         ftp->port.port = FTPD_PORT;
  172.  
  173.         log_tcp(tcb,"open FTP");
  174.         time(&t);
  175.         tprintf(ftp->control,banner,hostname,version,ctime(&t));
  176.         break;
  177.     case CLOSE_WAIT:
  178.         close_tcp(tcb);
  179.         break;
  180.     case CLOSED:
  181.         if(tcb != ftp_tcb)
  182.             log_tcp(tcb,"close FTP");
  183.         if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  184.             ftp_delete(ftp);
  185.         /* Check if server is being shut down */
  186.         if(tcb == ftp_tcb)
  187.             ftp_tcb = NULLTCB;
  188.         del_tcp(tcb);
  189.         break;
  190.     }
  191. }
  192.  
  193. /* FTP Server Control channel Receiver upcall handler */
  194. static
  195. void
  196. ftpscr(tcb,cnt)
  197. struct tcb *tcb;
  198. int16 cnt;
  199. {
  200.     register struct ftp *ftp;
  201.     char c;
  202.     struct mbuf *bp;
  203.     void ftpcommand();
  204.  
  205.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  206.         /* Unknown connection, just kill it */
  207.         close_tcp(tcb);
  208.         return;
  209.     }
  210.     for (;;){
  211.         switch(ftp->state){
  212.         case COMMAND_STATE:
  213.         /* Assemble an input line in the session buffer. Return if incomplete */
  214.         while(recv_tcp(tcb,&bp,1) == 1){
  215.             pullup(&bp,&c,1);
  216.             switch(c){
  217.             case '\r':    /* Strip cr's */
  218.                 continue;
  219.             case '\n':    /* Complete line; process it */
  220.                 ftp->buf[ftp->cnt] = '\0';
  221.                 ftpcommand(ftp);
  222.                 ftp->cnt = 0;
  223.                 goto again;
  224.             default:    /* Assemble line */
  225.                 if(ftp->cnt != LINELEN-1)
  226.                     ftp->buf[ftp->cnt++] = c;
  227.                 break;
  228.             }
  229.         }
  230.         /* else no linefeed present yet to terminate command */
  231.         return;
  232.         case SENDING_STATE:
  233.         case RECEIVING_STATE:
  234.         /* Leave commands pending on receive queue until
  235.          * present command is done
  236.          */
  237.         return;
  238.         }
  239. again:;
  240.     }
  241. }
  242.  
  243. /* FTP server data channel connection state change upcall handler */
  244. void
  245. ftpsds(tcb,old,new)
  246. struct tcb *tcb;
  247. char old,new;
  248. {
  249.     register struct ftp *ftp;
  250.  
  251.     if((ftp = (struct ftp *)tcb->user) == NULLFTP ||
  252.         !tcpval(ftp->control)){    /* control session existing? */
  253.         /* Unknown connection. Kill it */
  254.         del_tcp(tcb);
  255.         return;
  256.     }
  257.     if(new == CLOSED){
  258.         /* Clear only if another transfer hasn't already started */
  259.         if(ftp->data == tcb)
  260.             ftp->data = NULLTCB;
  261.         del_tcp(tcb);
  262.         if(tcb->reason != NORMAL){
  263.             /* Data connection was reset, complain about it */
  264.             tprintf(ftp->control,noconn);
  265.             /* And clean up */
  266.             if(ftp->fp != NULLFILE && ftp->fp != stdout)
  267.                 fclose(ftp->fp);
  268.             ftp->fp = NULLFILE;
  269.             ftp->state = COMMAND_STATE;
  270.             /* Kick command parser if something is waiting */
  271.             if(ftp->control->rcvcnt != 0)
  272.                 ftpscr(ftp->control,ftp->control->rcvcnt);
  273.             return;
  274.         }
  275.     }
  276.     if(ftp->state == SENDING_STATE && (old == FINWAIT1 || old == CLOSING)){
  277.         /* We've received an ack of our FIN while sending; we're done */
  278.         ftp->state = COMMAND_STATE;
  279.         tprintf(ftp->control,txok);
  280.         /* Kick command parser if something is waiting */
  281.         if(ftp->control->rcvcnt != 0)
  282.             ftpscr(ftp->control,ftp->control->rcvcnt);
  283.         return;
  284.     }
  285.     if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){
  286.         /* FIN received on incoming file */
  287. #ifdef    CPM
  288.         if(ftp->type == ASCII_TYPE)
  289.             putc(CTLZ,ftp->fp);
  290. #endif
  291.         close_tcp(tcb);
  292.         if(ftp->fp != stdout)
  293.             fclose(ftp->fp);
  294.         ftp->fp = NULLFILE;
  295.         ftp->state = COMMAND_STATE;
  296.         tprintf(ftp->control,rxok);
  297.         /* Kick command parser if something is waiting */
  298.         if(ftp->control->rcvcnt != 0)
  299.             ftpscr(ftp->control,ftp->control->rcvcnt);
  300.         return;
  301.     }
  302. }
  303.  
  304. /* Parse and execute ftp commands */
  305. static
  306. void
  307. ftpcommand(ftp)
  308. register struct ftp *ftp;
  309. {
  310.     void ftpdr(),ftpdt(),ftpsds();
  311.     char *cmd,*arg,*cp,**cmdp,*file;
  312.     char *pathname();
  313.     char *mode;
  314.     struct socket dport;
  315.     int i;
  316.  
  317. #ifndef CPM
  318.     FILE *dir();
  319. #endif
  320.  
  321.     cmd = ftp->buf;
  322.     if(ftp->cnt == 0){
  323.         /* Can't be a legal FTP command */
  324.         tprintf(ftp->control,badcmd);
  325.         return;
  326.     }
  327.  
  328. #ifdef    UNIX
  329.     /* Translate first word to lower case */
  330.     for(cp = cmd;*cp != ' ' && *cp != '\0';cp++)
  331.         *cp = tolower(*cp);
  332. #else
  333.     /* Translate entire buffer to lower case */
  334.     for(cp = cmd;*cp != '\0';cp++)
  335.         *cp = tolower(*cp);
  336. #endif
  337.  
  338.     /* Find command in table; if not present, return syntax error */
  339.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  340.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  341.             break;
  342.     if(*cmdp == NULLCHAR){
  343.         tprintf(ftp->control,badcmd);
  344.         return;
  345.     }
  346.     /* Allow only USER, PASS and QUIT before logging in */
  347.     if(ftp->cd == NULLCHAR || ftp->path[0] == NULLCHAR){
  348.         switch((int) (cmdp-commands)){
  349.         case USER_CMD:
  350.         case QUIT_CMD:
  351.             break;
  352.         case PASS_CMD:
  353.             if(ftp->username != NULLCHAR)
  354.                 break;            /* username entered */
  355.         default:
  356.             tprintf(ftp->control,notlog);
  357.             return;
  358.         }
  359.     }
  360.     arg = &cmd[strlen(*cmdp)];
  361.     while(*arg == ' ')
  362.         arg++;
  363.  
  364.     /* Execute specific command */
  365.     switch((int) (cmdp-commands)){
  366.     case USER_CMD:
  367.         if(ftp->username != NULLCHAR)
  368.             free(ftp->username);
  369.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  370.             close_tcp(ftp->control);
  371.             break;
  372.         }
  373.         strcpy(ftp->username,arg);
  374.         /* erase all user info from possible previous session */
  375.         for(i = 0; i < MAXPATH; i++){
  376.             if(ftp->path[i] != NULLCHAR){
  377.                 free(ftp->path[i]);
  378.                 ftp->path[i] = NULLCHAR;
  379.             }
  380.             ftp->perms[i] = 0;
  381.         }
  382.         if(ftp->cd != NULLCHAR){
  383.             free(ftp->cd);
  384.             ftp->cd = NULLCHAR;
  385.         }
  386.         tprintf(ftp->control,givepass);
  387.         break;
  388.     case TYPE_CMD:
  389.         switch(arg[0]){
  390.         case 'A':
  391.         case 'a':    /* Ascii */
  392.             ftp->type = ASCII_TYPE;
  393.             tprintf(ftp->control,typeok);
  394.             break;
  395.         case 'l':
  396.         case 'L':
  397.             while(*arg != ' ' && *arg != '\0')
  398.                 arg++;
  399.             if(*arg == '\0' || *++arg != '8'){
  400.                 tprintf(ftp->control,only8);
  401.                 break;
  402.             }    /* Note fall-thru */
  403.         case 'B':
  404.         case 'b':    /* Binary */
  405.         case 'I':
  406.         case 'i':    /* Image */
  407.             ftp->type = IMAGE_TYPE;
  408.             tprintf(ftp->control,typeok);
  409.             break;
  410.         default:    /* Invalid */
  411.             tprintf(ftp->control,badtype,arg);
  412.             break;
  413.         }
  414.         break;
  415.     case QUIT_CMD:
  416.         tprintf(ftp->control,bye);
  417.         close_tcp(ftp->control);
  418.         break;
  419.     case RETR_CMD:
  420.         /* Disk operation; return ACK now */
  421.         tcp_output(ftp->control);
  422.         file = pathname(ftp->cd,arg);
  423.         if(ftp->type == IMAGE_TYPE)
  424.             mode = binmode[READ_BINARY];
  425.         else
  426.             mode = "r";
  427.         if(!permcheck(ftp,RETR_CMD,file)){
  428.             tprintf(ftp->control,noperm);
  429.             free(file);
  430.             break;
  431.         }
  432.         if((ftp->fp = fopen(file,mode)) == NULLFILE){
  433.             tprintf(ftp->control,cantopen,file);
  434.             free(file);
  435.             break;
  436.         }
  437.         log_tcp(ftp->control,"RETR %s",file);
  438.         dport.address = ip_addr;
  439.         dport.port = FTPD_PORT;
  440.         ftp->state = SENDING_STATE;
  441.         tprintf(ftp->control,sending,"RETR",arg);
  442.         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  443.             0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  444.         if (ftp->filename != NULLCHAR) {
  445.             free(ftp->filename);
  446.             ftp->filename = NULLCHAR;
  447.         }
  448.         free(file);
  449.         break;
  450.     case STOR_CMD:
  451.         /* Disk operation; return ACK now */
  452.         tcp_output(ftp->control);
  453.         file = pathname(ftp->cd,arg);
  454.         if(ftp->type == IMAGE_TYPE)
  455.             mode = binmode[WRITE_BINARY];
  456.         else
  457.             mode = "w";
  458.         if(!permcheck(ftp,STOR_CMD,file)){
  459.             tprintf(ftp->control,noperm);
  460.             free(file);
  461.             break;
  462.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  463.             tprintf(ftp->control,cantmake,file);
  464.             free(file);
  465.             break;
  466.         }
  467.         log_tcp(ftp->control,"STOR %s",file);
  468.         dport.address = ip_addr;
  469.         dport.port = FTPD_PORT;
  470.         ftp->state = RECEIVING_STATE;
  471.         tprintf(ftp->control,sending,"STOR",arg);
  472.         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  473.             0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(char *)ftp);
  474.         if (ftp->filename != NULLCHAR)
  475.             free(ftp->filename);
  476.         ftp->filename = file;        /* save "current filename" */
  477.         break;
  478.     case PORT_CMD:
  479.         if(pport(&ftp->port,arg) == -1){
  480.             tprintf(ftp->control,badport);
  481.         } else {
  482.             tprintf(ftp->control,portok);
  483.         }
  484.         break;
  485. #ifndef CPM
  486.     case LIST_CMD:
  487.         /* Disk operation; return ACK now */
  488.         tcp_output(ftp->control);
  489.  
  490.         file = pathname(ftp->cd,arg);
  491.         if(!permcheck(ftp,LIST_CMD,file)){
  492.             tprintf(ftp->control,noperm);
  493.         } else if((ftp->fp = dir(file,1)) == NULLFILE){
  494.             tprintf(ftp->control,nodir,file);
  495.         } else {
  496.             dport.address = ip_addr;
  497.             dport.port = FTPD_PORT;
  498.             ftp->state = SENDING_STATE;
  499.             tprintf(ftp->control,sending,"LIST",file);
  500.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  501.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  502.         }
  503.         if (ftp->filename != NULLCHAR) {
  504.             free(ftp->filename);
  505.             ftp->filename = NULLCHAR;
  506.         }
  507.         free(file);
  508.         break;
  509.     case NLST_CMD:
  510.         /* Disk operation; return ACK now */
  511.         tcp_output(ftp->control);
  512.  
  513.         file = pathname(ftp->cd,arg);
  514.         if(!permcheck(ftp,LIST_CMD,file)){
  515.             tprintf(ftp->control,noperm);
  516.         } else if((ftp->fp = dir(file,0)) == NULLFILE){
  517.             tprintf(ftp->control,nodir,file);
  518.         } else {
  519.             dport.address = ip_addr;
  520.             dport.port = FTPD_PORT;
  521.             ftp->state = SENDING_STATE;
  522.             tprintf(ftp->control,sending,"NLST",file);
  523.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  524.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  525.         }
  526.         if (ftp->filename != NULLCHAR) {
  527.             free(ftp->filename);
  528.             ftp->filename = NULLCHAR;
  529.         }
  530.         free(file);
  531.         break;
  532.     case CWD_CMD:
  533.         tcp_output(ftp->control);    /* Disk operation; return ACK now */
  534.  
  535.         file = pathname(ftp->cd,arg);
  536.         if(!permcheck(ftp,RETR_CMD,file)){
  537.             tprintf(ftp->control,noperm);
  538.             free(file);
  539. #if    (defined(MSDOS) || defined(ATARI_ST))
  540.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  541.         } else if(strcmp(file,"\\") == 0 ||
  542.               (isalpha(file[0]) && strcmp(file + 1,":\\") == 0) ||
  543.               access(file,0) == 0){
  544. #else
  545.         } else if(access(file,0) == 0){ /* See if it exists */
  546. #endif
  547.             /* Succeeded, record in control block */
  548.             free(ftp->cd);
  549.             ftp->cd = file;
  550.             tprintf(ftp->control,pwdmsg,file);
  551.         } else {
  552.             /* Failed, don't change anything */
  553.             tprintf(ftp->control,nodir,file);
  554.             free(file);
  555.         }
  556.         break;
  557.     case XPWD_CMD:
  558.     case PWD_CMD:
  559.         tprintf(ftp->control,pwdmsg,ftp->cd);
  560.         break;
  561. #else
  562.     case LIST_CMD:
  563.     case NLST_CMD:
  564.     case CWD_CMD:
  565.     case XPWD_CMD:
  566.     case PWD_CMD:
  567. #endif
  568.     case ACCT_CMD:
  569.         tprintf(ftp->control,unimp);
  570.         break;
  571.     case DELE_CMD:
  572.         file = pathname(ftp->cd,arg);
  573.         if(!permcheck(ftp,DELE_CMD,file)){
  574.             tprintf(ftp->control,noperm);
  575.         } else if(unlink(file) == 0){
  576.             tprintf(ftp->control,deleok);
  577.         } else {
  578.             tprintf(ftp->control,delefail);
  579.         }
  580.         free(file);
  581.         break;
  582.     case PASS_CMD:
  583.         tcp_output(ftp->control);    /* Send the ack now */
  584.         ftplogin(ftp,arg);
  585.         break;
  586. #ifndef CPM
  587.     case XMKD_CMD:
  588.     case MKD_CMD:
  589.         file = pathname(ftp->cd,arg);
  590.         if(!permcheck(ftp,MKD_CMD,file)){
  591.             tprintf(ftp->control,noperm);
  592.         } else if(mkdir(file,0777) == 0){
  593.             tprintf(ftp->control,mkdok,file);
  594.         } else {
  595.             tprintf(ftp->control,cantmake,file);
  596.         }
  597.         free(file);
  598.         break;
  599.     case XRMD_CMD:
  600.     case RMD_CMD:
  601.         file = pathname(ftp->cd,arg);
  602.         if(!permcheck(ftp,RMD_CMD,file)){
  603.             tprintf(ftp->control,noperm);
  604.         } else if(rmdir(file) == 0){
  605.             tprintf(ftp->control,deleok);
  606.         } else {
  607.             tprintf(ftp->control,delefail);
  608.         }
  609.         free(file);
  610.         break;
  611.     case STRU_CMD:
  612.         if(tolower(arg[0]) != 'f')
  613.             tprintf(ftp->control,unsupp);
  614.         else
  615.             tprintf(ftp->control,okay);
  616.         break;
  617.     case MODE_CMD:
  618.         if(tolower(arg[0]) != 's')
  619.             tprintf(ftp->control,unsupp);
  620.         else
  621.             tprintf(ftp->control,okay);
  622.         break;
  623. #endif
  624. #if (defined(ATARI_ST))
  625.     case XATR_CMD:
  626.         {
  627.         struct socket sock;    /* misuse, but saves code */
  628.         int fd;
  629.         unsigned int info[2];
  630.  
  631.         if(pport(&sock,arg) == -1){
  632.             tprintf(ftp->control,badxatr);
  633.         } else {
  634.             if((file = ftp->filename) == NULLCHAR ||
  635.                !permcheck(ftp,XATR_CMD,file)){
  636.                 tprintf(ftp->control,noperm);
  637.             } else {
  638.             Fattrib(file,1,(int) 0);    /* remove "read only" */
  639.             if ((fd = Fopen(file,2)) >= 0) {
  640.                 info[0] = loword(sock.address);
  641.                 info[1] = hiword(sock.address);
  642.                 Fdatime(info,fd,1);        /* set date/time */
  643.                 Fclose(fd);
  644.                 Fattrib(file,1,(int) sock.port);
  645.                 tprintf(ftp->control,xatrok,file);
  646.             } else {
  647.                 tprintf(ftp->control,xatrfail,file);
  648.             }
  649.             }
  650.         }
  651.         }
  652.         break;
  653. #endif
  654.     }
  655. }
  656. static
  657. int
  658. pport(sock,arg)
  659. struct socket *sock;
  660. char *arg;
  661. {
  662.     int32 n;
  663.     int i;
  664.  
  665.     n = 0;
  666.     for(i=0;i<4;i++){
  667.         n = atoi(arg) + (n << 8);
  668.         if((arg = index(arg,',')) == NULLCHAR)
  669.             return -1;
  670.         arg++;
  671.     }
  672.     sock->address = n;
  673.     n = atoi(arg);
  674.     if((arg = index(arg,',')) == NULLCHAR)
  675.         return -1;
  676.     arg++;
  677.     n = atoi(arg) + (n << 8);
  678.     sock->port = n;
  679.     return 0;
  680. }
  681. /* Attempt to log in the user whose name is in ftp->username and password
  682.  * in pass
  683.  */
  684. static
  685. ftplogin(ftp,pass)
  686. struct ftp *ftp;
  687. char *pass;
  688. {
  689.     char buf[256],*cp,*cp1,*getnenv();
  690.     FILE *fp;
  691.     int anony = 0;
  692.     int i;
  693.  
  694.     if((fp = fopen(getnenv(USERFILE),"r")) == NULLFILE){
  695.         /* Userfile doesn't exist */
  696.         tprintf(ftp->control,noperm);
  697.         return;
  698.     }
  699.     while(fgets(buf,sizeof(buf),fp) != NULLCHAR){
  700.         if(buf[0] == '#')
  701.             continue;    /* Comment */
  702.         if((cp = index(buf,' ')) == NULLCHAR)
  703.             /* Bogus entry */
  704.             continue;
  705.         *cp++ = '\0';        /* Now points to password */
  706.         if(strcmp(ftp->username,buf) == 0)
  707.             break;        /* Found user name */
  708.     }
  709.     if(feof(fp)){
  710.         /* User name not found in file */
  711.         fclose(fp);
  712.         tprintf(ftp->control,noperm);
  713.         return;
  714.     }
  715.     fclose(fp);
  716.     /* Look for space after password field in file */
  717.     if((cp1 = index(cp,' ')) == NULLCHAR){
  718.         /* Invalid file entry */
  719.         tprintf(ftp->control,noperm);
  720.         return;
  721.     }
  722.     *cp1++ = '\0';    /* Now points to 1st path field */
  723.     if(strcmp(cp,"*") == 0)
  724.         anony = 1;    /* User ID is password-free */
  725.     else if(strcmp(cp,pass) != 0){
  726.         /* Password required, but wrong one given */
  727.         tprintf(ftp->control,noperm);
  728.         return;
  729.     }
  730.  
  731.     for(i = 0; i < MAXPATH; i++){
  732.         if((cp = index(cp1,' ')) == NULLCHAR){
  733.             /* Permission field missing, assume end of line */
  734.             break;
  735.         }
  736.         *cp++ = '\0';    /* now points to permission field */
  737.         ftp->path[i] = malloc((unsigned)strlen(cp1)+1);
  738.         strcpy(ftp->path[i],cp1);
  739.         /* set the permission bits */
  740.         ftp->perms[i] = atoi(cp);
  741.         if((cp1 = index(cp,' ')) == NULLCHAR){
  742.             /* no next path field, assume end of line */
  743.             break;
  744.         }
  745.         *cp1++ = '\0';    /* cp1 now points to next path field */
  746.     }
  747.  
  748.     /* Set up current directory = LAST specified path */
  749.     for(i = MAXPATH - 1; i >= 0; i--)
  750.         if(ftp->perms[i])
  751.             break;
  752.     if(i < 0){
  753.         tprintf(ftp->control,noperm);
  754.         return;
  755.     }
  756.  
  757.     ftp->cd = malloc((unsigned)strlen(ftp->path[i])+1);
  758.     strcpy(ftp->cd,ftp->path[i]);
  759.  
  760.     tprintf(ftp->control,logged);
  761.     if(!anony)
  762.         log_tcp(ftp->control,"%s logged in",ftp->username);
  763.     else
  764.         log_tcp(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  765. }
  766.  
  767. #if    (defined(MSDOS) || defined(ATARI_ST))
  768. /* Illegal characters in a DOS filename */
  769. char badchars[] = "*?\"[]|<>+=;,";
  770. #endif
  771.  
  772. /* Return 1 if the file operation is allowed, 0 otherwise */
  773. permcheck(ftp,op,file)
  774. struct ftp *ftp;
  775. int op;
  776. char *file;
  777. {
  778.     unsigned char *cp;
  779.     int i;
  780.  
  781.     if(file == NULLCHAR || ftp->path[0] == NULLCHAR)
  782.         return 0;    /* Probably hasn't logged in yet */
  783.  
  784. #if    (defined(MSDOS) || defined(ATARI_ST))
  785.     /* Check for characters illegal in MS-DOS file names */
  786.     /* "*" and "?" are only allowed in LIST commands */
  787.     for(cp = (unsigned char *) file;*cp != '\0';cp++){
  788.         if(*cp <= ' ' ||
  789.            index(badchars + 2 * (op == LIST_CMD),*cp) != NULLCHAR)
  790.             return 0;
  791.     }
  792.     if(file[0] != '\0' && file[1] != '\0' &&
  793.        index(file + 2,':') != NULLCHAR)
  794.         return 0;
  795. #endif
  796.  
  797. #if    (defined(AMIGA) || defined(MAC))
  798. #else
  799.     /* The target file must be under the user's allowed search path */
  800.     for(i = 0; i < MAXPATH; i++)
  801.         if(ftp->path[i] != NULLCHAR &&
  802.            strncmp(file,ftp->path[i],strlen(ftp->path[i])) == 0)
  803.             break;
  804.  
  805.     if(i == MAXPATH)
  806.         return 0;
  807. #endif
  808.  
  809.     switch(op){
  810.     case LIST_CMD:
  811.     case RETR_CMD:
  812.         /* User must have permission to read files */
  813.         if(ftp->perms[i] & FTP_READ)
  814.             return 1;
  815.         return 0;
  816.     case DELE_CMD:
  817.     case RMD_CMD:
  818.         /* User must have permission to (over)write files */
  819.         if(ftp->perms[i] & FTP_WRITE)
  820.             return 1;
  821.         return 0;
  822.     case STOR_CMD:
  823.     case MKD_CMD:
  824.         /* User must have permission to (over)write files, or permission
  825.          * to create them if the file doesn't already exist
  826.          */
  827.         if(ftp->perms[i] & FTP_WRITE)
  828.             return 1;
  829.         if(access(file,2) == -1 && (ftp->perms[i] & FTP_CREATE))
  830.             return 1;
  831.         return 0;
  832. #if (defined(ATARI_ST))
  833.     case XATR_CMD:
  834.         /* User must be able to create files someway
  835.          * Probably, permission check is not necessary at all
  836.          */
  837.         if(ftp->perms[i] & (FTP_WRITE|FTP_CREATE))
  838.             return 1;
  839.         return 0;
  840. #endif
  841.     }
  842.     return 0;    /* "can't happen" -- keep lint happy */
  843. }
  844.